# -*- coding: utf-8 -*-
"""
---------------------------------------
@File    ：fetch_and_update_ip.py
@Author  ：liyunhui
@Date    ：2024/9/14 上午10:47 
@explain : 重新拨号后调用该服务更新IP
---------------------------------------
"""
import time
import requests
import argparse
import traceback
import subprocess
import logging.handlers


def str2bool(v):
    if isinstance(v, bool):
        return v
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError('需要布尔值!')


def parse_args():
    parser = argparse.ArgumentParser(description="获取并更新IP脚本")
    parser.add_argument('--status', type=str2bool, required=True, help='ADSL状态: True or False')
    return parser.parse_args()


def create_logging(log_name):
    """
    自定义日志模块
    :return:
    """
    # 自定义日志文件名称
    log_filename = f"/LYH/{log_name}.log"

    # 创建一个TimedRotatingFileHandler，指定编码为utf-8
    handler = logging.handlers.TimedRotatingFileHandler(
        filename=log_filename,
        when='midnight',  # 每天0时自动构建新日志文件
        interval=1,
        backupCount=15,  # 保留15天的日志，超过则自动删除
        encoding='utf-8'  # 指定编码为utf-8
    )
    # 设置日志文件后缀格式
    handler.suffix = "%Y-%m-%d"
    # 创建一个日志格式化器
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(module)s.%(funcName)s:%(lineno)d - %(message)s')
    # 将格式化器添加到处理器
    handler.setFormatter(formatter)
    # 创建一个新的日志记录器
    cus_logger = logging.getLogger(log_name)
    cus_logger.setLevel(logging.INFO)
    # 如果记录器没有处理器，则添加处理器
    if not cus_logger.handlers:
        cus_logger.addHandler(handler)
    return cus_logger


logger = create_logging("update_ip")


# 获取内网 IP
def get_intranet_ip():
    try:
        result = subprocess.check_output("hostname -I", shell=True, timeout=10)
        return result.decode('utf-8').strip().split()[0]
    except Exception as e:
        logger.error(f"Error getting intranet IP: {str(traceback.format_exc())}")
        return None


def retry_adsl_call():
    """
    重新调用拨号脚本
    :return:
    """
    try:
        logger.info("正在重新拨号...")
        # 依次执行各个命令
        subprocess.run(['sudo', '/usr/sbin/adsl-stop'], check=True)
        subprocess.run(['sudo', '/usr/bin/pkill', 'ppp'], check=True)
        subprocess.run(['sudo', '/usr/bin/pkill', 'pppd'], check=True)
        # 等待 5 秒钟
        time.sleep(5)
        subprocess.run(['sudo', '/usr/sbin/adsl-start'], check=True)
        logger.info("拨号成功，正在重新尝试获取公网IP...")
    except subprocess.CalledProcessError as e:
        logger.error(f"拨号服务失败: {e}")
    except Exception as e:
        logger.error(f"拨号服务异常: {str(traceback.format_exc())}")


def get_public_ip():
    """
    获取公网IP，当获取公网失败次数超过3次后，重新调用拨号脚本
    :return:
    """
    max_retry = 3
    total_retry = 0
    while total_retry < max_retry:
        try:
            response = requests.get('http://ipinfo.io/ip', timeout=10)
            if response.status_code == 200:
                public_ip = response.text.strip()
                logger.info(f"成功获取到公网IP: {public_ip}")
                return public_ip
            else:
                logger.error(f"获取公网IP失败，状态码: {response.status_code}")
        except Exception as e:
            logger.error(
                f"获取公网IP失败, 正在重试... {total_retry + 1}/{max_retry}: {str(traceback.format_exc())}"
            )
        total_retry += 1
        time.sleep(5)  # 每次失败后等待5秒重试

        # 超过最大重试次数后重新拨号
        if total_retry >= max_retry:
            logger.info("获取公网IP失败次数超过3次，重新启动拨号服务...")
            retry_adsl_call()
            total_retry = 0  # 重置重试次数，继续获取公网 IP


# 调用API更新 IP
def update_ip(intranet_ip, public_ip, status):
    url = "http://47.238.104.195:12318/api/ip_pool/ip_update"
    payload = {
        "intranet_ip": intranet_ip,
        "public_ip": public_ip,
        "status": status
    }
    try:
        response = requests.post(url, data=payload)
        if response.status_code == 200:
            logger.info(f"IP updated successfully ---> {str(payload)}")
        else:
            logger.info(f"IP updated Failed ---> {str(payload)}")
            logger.info(f"Failed to update IP: {response.text}")
    except Exception as e:
        logger.error(f"Error updating IP: {str(traceback.format_exc())}")


if __name__ == "__main__":
    # 解析命令行参数
    args = parse_args()
    status = args.status
    logger.info(f'status: {status}')
    # 获取内网 IP
    intranet_ip = get_intranet_ip()
    if intranet_ip is None:
        logger.error("无法获取内网IP，退出！")
        exit(1)

    # 获取公网 IP，直到成功为止
    public_ip = get_public_ip()
    # 调用 API 更新 IP
    update_ip(intranet_ip, public_ip, status)
